//
//  BGOPersistenceManager.m
//  Bango iOS API
//
//  Created by William Shakour on September 2, 2011.
//  Copyrights © 2011 Bango - http://www.bango.net/ - All rights reserved.
//

#import "BGOPersistenceManager.h"
#import "BGOProfilePersistent.h"
#import "BGODatabaseConstants.h"
#import "BGOFMDatabase.h"
#import "BGOProfile.h"
#import "BGOEvent.h"
#import "BGOEventParameter.h"
#import "BGOAnalyticsConstants.h"

#define NIL_IS_ES(a) a==nil?@"":a

@interface BGOPersistenceManager (Private)
-(NSString *)applicationDocumentsDirectory;
-(NSString *)applicationCacheDirectory;
-(void)addEventsToProfileForPost:(BGOProfile *)profile chunked:(BOOL)inChunks;
//-(void)test;
@end

@implementation BGOPersistenceManager

@dynamic profileTableName;
@dynamic eventTableName;
@dynamic eventParameterTableName;
@dynamic databaseName;
@dynamic debugLog;
@synthesize postChunkSize = _postChunkSize;
@synthesize _profilePersistent;

//-(void)test {
//  [self logProfileWithSdkType:1
//                   sdkVersion:@"version"
//                applicationId:@"test"
//              applicationType:@"application type"
//           applicationChannel:@"application channel"
//           applicationVersion:@"application version"
//                       userId:@"user id"
//           sessionRestartTime:[NSDate date]
//               connectionType:1
//                    isRoaming:NO
//               locationSource:@"Some"
//             locationAltitude:10
//             locationLatitude:20
//            locationLongitude:10
//             locationAccuracy:10
//                  netOperator:@"operator"
//                 operatorName:@"operator name"
//           operatorCountryIso:@"UK"
//                  simOperator:@"simop"
//                        brand:@"brand"
//                       device:@"device"
//                        model:@"model"
//                      product:@"product"
//                      version:@"version"
//                      buildId:@"buildId"
//       operatorConnectionType:1];
//  
//  NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
//  [dictionary setObject:@"test" forKey:@"test"];
//  [dictionary setObject:@"test1" forKey:@"test1"];
//  
//  int eventId = [self logEventWithConnectionType:1
//                          operatorConnectionType:1
//                                      errorLevel:0
//                                       eventName:@"event name"
//                                     eventDetail:@"event detail"
//                                      eventValue:@"event value"
//                                         errorId:@"error id"
//                                       eventTime:[NSDate date]
//                                  eventLocalTime:[NSDate date]
//                                 eventParameters:dictionary];
//  NSLog(@"%d", eventId);  
//}

-(id)init {
    if ((self = [super init]) != nil) {
        _database = [[BGOFMDatabase databaseWithPath:self.databaseName] retain];
        _database.shouldCacheStatements = YES;
        _persistDatabaseConnection = YES;
        
        [self openDatabaseWithForce:YES];
        _postChunkSize = DEFAULT_POST_CHUNK_SIZE;        
    }
    
    return self;
}

-(void)setDebugLog:(BOOL)debugLog {
    _database.logsErrors = debugLog;
    _database.traceExecution = debugLog;
}

-(BOOL)isDebugLog {
    return _database.logsErrors || _database.traceExecution;
}

-(NSString *)profileTableName {
    return [NSString stringWithFormat:@"%@_%d", DATABASE_TABLENAMEPROFILE, DATABASE_VERSION];
}

-(NSString *)eventTableName {
    return [NSString stringWithFormat:@"%@_%d", DATABASE_TABLENAMEEVENT, DATABASE_VERSION];
}

-(NSString *)eventParameterTableName {
    return [NSString stringWithFormat:@"%@_%d", DATABASE_TABLENAMEEVENTPARAMETER, DATABASE_VERSION];
}

-(NSString *)profilePersistentTableName{
    return [NSString stringWithFormat:@"%@_%d", DATABASE_TABLENAMEPROFILEPERSISTENT, DATABASE_VERSION];
}

-(NSString *)databaseName {
    NSString *databaseFilePath = [[self applicationCacheDirectory] stringByAppendingPathComponent:DATABASE_NAME];
    
    // always log database location - at this point the _database class is nil 
    // asking it if debug is enabled is pointless
    NSLog(@"Database is located at %@", databaseFilePath);
    
    return databaseFilePath;
}

-(void)compactDatabase {
    //[_database executeUpdate:@"VACUUM;"];
}

-(void)createTables {
    @synchronized(self) {
        [_database executeUpdate:[NSString stringWithFormat:DATABASE_TABLECREATEPROFILE, self.profileTableName]];
        [_database executeUpdate:[NSString stringWithFormat:DATABASE_TABLECREATEEVENT, self.eventTableName]];
        [_database executeUpdate:[NSString stringWithFormat:DATABASE_TABLECREATEEVENTPARAMETER, self.eventParameterTableName]];
        [_database executeUpdate:[NSString stringWithFormat:DATABASE_TABLECREATEPROFILEPERSISTENT, self.profilePersistentTableName]];
        
        [self compactDatabase];
    }
}

-(void)dropTables {
    @synchronized(self) {
        [_database executeUpdate:@"PRAGMA writable_schema = 1;"];
        [_database executeUpdate:[NSString stringWithFormat:DATABASE_DELETETABLES, DATABASE_TABLENAMEPREFIX]];
        [_database executeUpdate:@"PRAGMA writable_schema = 0;"];
        
        [self compactDatabase];
        
        [_database executeUpdate:@"PRAGMA INTEGRITY_CHECK;"];
    }
}

-(void)openDatabase {
    [self openDatabaseWithForce:NO];
}

-(void)openDatabaseWithForce:(BOOL)forceOpen {
    BOOL openDb = NO;
    
    if (_database != nil) {
        if (forceOpen || !_isOpen) {
            openDb = YES;
        }
    } else { // this should never happen
        if (!_persistDatabaseConnection) {
            openDb = YES;
        }
    }
    
    @synchronized(self) {
        if (openDb) {
            _isOpen = [_database openWithFlags:SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_FULLMUTEX];
        }
        
        if (_insertProfileStatement == nil) {
            _insertProfileStatement = [[NSString stringWithFormat:DATABASE_CREATEPROFILE,
                                        self.profileTableName] retain];
        }
        
        if (_insertEventStatement == nil) {
            _insertEventStatement = [[NSString stringWithFormat:DATABASE_CREATEEVENT,
                                      self.eventTableName] retain];
        }
        
        if (_insertEventParameterStatement == nil) {
            _insertEventParameterStatement = [[NSString stringWithFormat:DATABASE_CREATEEVENTPARAMETER,
                                               self.eventParameterTableName] retain];
        }
        
        if(_insertProfilePersistentStatement == nil){
            _insertProfilePersistentStatement = [[NSString stringWithFormat:DATABASE_CREATEPROFILEPERSISTENT, self.profilePersistentTableName] retain];
        }
        
        if (_deleteProfilePersistentStatement == nil) {
            _deleteProfilePersistentStatement = [[NSString stringWithFormat:DATABASE_DELETEPROFILEPERSISTENT, self.profilePersistentTableName] retain];
        }
        
        
        
        // This does not create the tables every time but should really check update 
        // and remove existing tables in that case [self dropTables];
        [self createTables];
    }
}


-(void)closeDatabase
{
    if (_database != nil) {
        if (!_persistDatabaseConnection && _isOpen && !_openLogProfile &&
            !_openUpdateProfileLocation && !_openLogEvent &&
            !_openProfilesForPost && !_openEventsForPost && !_openEventsPosted &&
            !_openDeleteEventsPosted) {
            @synchronized(self) {
                _isOpen = ![_database close];
            }
        }
    }
}

-(void)logProfileWithSdkType:(int)sdkType
                  sdkVersion:(NSString *)sdkVersion
               applicationId:(NSString *)applicationId
             applicationType:(NSString *)applicationType
          applicationChannel:(NSString *)applicationChannel
          applicationVersion:(NSString *)applicationVersion
                      userId:(NSString *)userId
          sessionRestartTime:(int)sessionRestartTime
                     created:(NSDate *)created
              connectionType:(NSString *)connectionType
                   isRoaming:(BOOL)isRoaming
              locationSource:(NSString *)locationSource
            locationAltitude:(double)locationAltitude
            locationLatitude:(double)locationLatitude
           locationLongitude:(double)locationLongitude
            locationAccuracy:(double)locationAccuracy
                 netOperator:(NSString *)netOperator
                operatorName:(NSString *)operatorName
          operatorCountryIso:(NSString *)operatorCountryIso
                 simOperator:(NSString *)simOperator
                       brand:(NSString *)brand
                      device:(NSString *)device
                   ipAddress:(NSString *)ipAddress
                       model:(NSString *)model
                     product:(NSString *)product
                     version:(NSString *)version
                     buildId:(NSString *)buildId
      operatorConnectionType:(NSString *)operatorConnectionType
                 bangoUserId:(NSString *)bangoUserId{
    @synchronized(self) {
        [self openDatabase];
        
        _openLogProfile = YES;
        [_database executeUpdate:_insertProfileStatement, [NSNumber numberWithInt:sdkType],
         sdkVersion, NIL_IS_ES(applicationId), NIL_IS_ES(applicationType),
         NIL_IS_ES(applicationChannel), NIL_IS_ES(applicationVersion), 
         NIL_IS_ES(userId), [NSNumber numberWithInt:sessionRestartTime],
         created, connectionType, 
         [NSNumber numberWithBool:isRoaming], NIL_IS_ES(locationSource), 
         [NSNumber numberWithDouble:locationAltitude],
         [NSNumber numberWithDouble:locationLatitude],
         [NSNumber numberWithDouble:locationLongitude], 
         [NSNumber numberWithDouble:locationAccuracy],
         NIL_IS_ES(netOperator), NIL_IS_ES(operatorName),
         NIL_IS_ES(operatorCountryIso), NIL_IS_ES(simOperator), NIL_IS_ES(brand),
         NIL_IS_ES(device), NIL_IS_ES(model), NIL_IS_ES(product), NIL_IS_ES(version), 
         NIL_IS_ES(buildId), NIL_IS_ES(operatorConnectionType),
         [NSNumber numberWithDouble:0]];
        
        _profileId = (int)[_database lastInsertRowId];
        
        _openLogProfile = NO;
        
        [self closeDatabase];
        [self LogProfilePersistent:bangoUserId];
    }
}

-(void)updateProfileLocationWithLocationLatitude:(double)locationLatitude
                               locationLongitude:(double)locationLongitude 
                                locationAltitude:(double)locationAltitude
                                locationAccuracy:(double)locationAccuracy {
    @synchronized(self) {
        [self openDatabase];
        
        _openUpdateProfileLocation = YES;
        [_database executeUpdate:[NSString stringWithFormat:DATABASE_UPDATEPROFILELOCATION,
                                  self.profileTableName], 
         [NSNumber numberWithDouble:locationLatitude],
         [NSNumber numberWithDouble:locationLongitude], 
         [NSNumber numberWithDouble:locationAltitude],
         [NSNumber numberWithDouble:locationAccuracy],
         [NSNumber numberWithInt:_profileId]];
        
        _openUpdateProfileLocation = NO;
        
        [self closeDatabase];
    }
}

-(void)logEventWithConnectionType:(NSString *)connectionType
           operatorConnectionType:(NSString *)operatorConnectionType
                       errorLevel:(int)errorLevel
                        eventName:(NSString *)eventName
                      eventDetail:(NSString *)eventDetail
                       eventValue:(NSString *)eventValue
                          errorId:(NSString *)errorId
                        eventTime:(NSDate *)eventTime
                  eventParameters:(NSDictionary *)eventParameters
{
    int eventId = -1;
    
    @synchronized(self) {
        [self openDatabase];
        
        _openLogEvent = YES;
        
        [_database executeUpdate:_insertEventStatement,
         [NSNumber numberWithInt:_profileId], NIL_IS_ES(eventName),
         NIL_IS_ES(eventDetail), NIL_IS_ES(eventValue),
         NIL_IS_ES(errorId), eventTime, connectionType,
         NIL_IS_ES(operatorConnectionType), [NSNumber numberWithInt:errorLevel], 
         [NSNumber numberWithInt:0]];
        
        eventId = (int)[_database lastInsertRowId];
        
        
        [self logEventParameterWithEventId:eventId eventParameters:eventParameters];
        
        _openLogEvent = NO;
        
        [self closeDatabase];
    }
}

-(void)logEventParameterWithEventId:(int)eventId
                    eventParameters:(NSDictionary *)eventParameters {
    NSString *eventParameterValue = nil;
    if (eventParameters != nil) {
        @synchronized(self) {
            for (NSString *eventParameterName in [eventParameters allKeys]) {
                eventParameterValue = [eventParameters valueForKey:eventParameterName];
                
                [_database executeUpdate:_insertEventParameterStatement, 
                 [NSNumber numberWithInt:eventId], eventParameterName,
                 NIL_IS_ES(eventParameterValue)];
            }
        }
    }
}

-(BOOL)updateEventsPostInProgress:(BGOProfile *)profile {
    BOOL updated = NO;
    @synchronized(self) {
        NSMutableString *profileEventIds = [NSMutableString string];
        
        for (BGOEvent *event in profile.events) {
            [profileEventIds appendFormat:@"%d,", event.id];
        }
        
        if (profileEventIds.length > 0) {
            [profileEventIds replaceCharactersInRange:NSMakeRange(profileEventIds.length - 1, 1)
                                           withString:@""];
        }
        
        [self openDatabase];
        
        _openEventsPosted = YES;
        if ([_database beginTransaction]) {
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_UPDATEEVENTPOSTINPROGRESS,
                                      self.eventTableName, profileEventIds]];
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_UPDATEPROFILEPOSTINPROGRESS,
                                      self.profileTableName], 
             [NSNumber numberWithInt:profile.id]];
            
            if ([_database commit]) {
                updated = YES;
            } else {
                NSLog(@"Error commiting trasaction");
            }
        } else {
            NSLog(@"Error beginning trasaction");
        }
        
        _openEventsPosted = NO;
        
        [self closeDatabase];
    }
    return updated;
}
-(BOOL)updatePostedStatusOnTransmissionFail:(int)profileId{
    BOOL updated = NO;
    @synchronized(self){
        _openEventsPosted = YES;
        
        if([_database beginTransaction]){
            NSNumber *boxedProfileId = [NSNumber numberWithInt:profileId];
            
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_UPDATEEVENT_TRANSMISSIONFAIL, self.eventTableName],boxedProfileId];
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_UPDATEPROFILE_TRANSMISSIONFAIL, self.profileTableName], boxedProfileId];
            
            if([_database commit]){
                updated = YES;
            } else{
                NSLog(@"Error Committing transaction");
            }
        }else{
            NSLog(@"Error beginning transaction");
        }
        _openEventsPosted = NO;
        [self closeDatabase];
    }
    return updated;
}


-(BOOL)updateEventsPosted:(int)profileId {
    BOOL updated = NO;
    @synchronized(self) {
        [self openDatabase];
        
        _openEventsPosted = YES;
        
        if ([_database beginTransaction]) {
            NSNumber *boxedProfileId = [NSNumber numberWithInt:profileId];
            
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_UPDATEEVENTPOSTED,
                                      self.eventTableName], boxedProfileId];
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_UPDATEPROFILEPOSTED,
                                      self.profileTableName, self.eventTableName], 
             boxedProfileId, boxedProfileId];
            
            if ([_database commit]) {
                updated = YES;
            } else {
                NSLog(@"Error committing transaction");
            }
        } else {
            NSLog(@"Error beginning transaction");
        }
        
        _openEventsPosted = NO;
        
        [self closeDatabase];
    }
    
    return updated;
}

-(BOOL)deleteEventsPosted:(int)profileId {
    BOOL deleted = NO;
    @synchronized(self) {
        [self openDatabase];
        
        _openDeleteEventsPosted = YES;
        
        if ([_database beginTransaction]) {
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_DELETEEVENTPARAMETER,
                                      self.eventParameterTableName, self.eventTableName],
             [NSNumber numberWithInt:profileId]];
            [_database executeUpdate:[NSString stringWithFormat:DATABASE_DELETEEVENT,
                                      self.eventTableName], 
             [NSNumber numberWithInt:profileId]];
            
            if (![self isCurrentProfile:profileId]) {
                [_database executeUpdate:[NSString stringWithFormat:DATABASE_DELETEPROFILE, 
                                          self.profileTableName], 
                 [NSNumber numberWithInt:profileId]];
            }
            
            if ([_database commit]) {
                deleted = YES;
            } else {
                NSLog(@"Error committing transaction");
            }
        } else {
            NSLog(@"Error beginning transaction");
        }
        
        _openDeleteEventsPosted = NO;
        
        [self closeDatabase];
    }
    
    return deleted;
}

-(NSArray *)getProfilesForPost:(BOOL)notCurrentProfile bangoUserId:(NSString *)bangoUserId {
    return [self getProfilesForPost:notCurrentProfile chunked:NO bangoUserId:bangoUserId];
}

-(BGOProfilePersistent *)GetProfilePersistent{
    BGOProfilePersistent *profilePersistent = [[BGOProfilePersistent alloc] init];
    @synchronized(self){
        BGOFMResultSet *result = [_database executeQuery:[NSString stringWithFormat:DATABASE_SELECTPOSTPROFILEPERSISTENT, self.profilePersistentTableName]];
        if(result != nil){
            NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
            [dateFormat setDateFormat:@"yyyy MM dd HH:mm:ss"];
            
            while ([result next]) {
                profilePersistent.BangoUserId = [result stringForColumn:DATABASE_COLUMN_PROFILEPERSISTENT_BANGOUSERID];
                NSString *currentDate = [result stringForColumn:DATABASE_COLUMN_PROFILEPERSISTENT_DATE];
                int timeInSeconds = BGO_SETTING_BANGOUSERID_EXPIRY_MILLISECONDS / 1000;
                profilePersistent.Date = [[dateFormat dateFromString:currentDate] dateByAddingTimeInterval:timeInSeconds];
                
            }
        }
    }
    return profilePersistent;
}

-(void)LogProfilePersistent:(NSString *)bangoUserId{
    NSString *currentBangoUserId = nil;
    @synchronized(self){
        [self openDatabase];
        if(bangoUserId != nil){
            _openLogProfile = YES;
            BGOFMResultSet *result = [_database executeQuery:[NSString stringWithFormat:DATABASE_SELECTPOSTPROFILEPERSISTENT, self.profilePersistentTableName]];
            if (result != nil) {
                while ([result next]) {
                    currentBangoUserId = [result stringForColumn:DATABASE_COLUMN_PROFILEPERSISTENT_BANGOUSERID];
                }
            }
            if (![bangoUserId isEqualToString:currentBangoUserId]) {
                if ([_database beginTransaction]) {
                    [_database executeUpdate:_deleteProfilePersistentStatement];
                    NSLocale *currentLocale = [NSLocale currentLocale];
                    [_database executeUpdate:_insertProfilePersistentStatement, bangoUserId, [[NSDate date] descriptionWithLocale:currentLocale]];
                    
                    if ([_database commit]) {
                        NSLog(@"BangoUserID Logged");
                    }
                    else{
                        NSLog(@"Error Logging BangoUserID");
                    }
                }
                else{
                    NSLog(@"Error starting transaction");
                }
            }
            _openLogProfile = NO;
        }
        [self closeDatabase];
    }
}

-(NSArray *)getProfilesForPost:(BOOL)notCurrentProfile chunked:(BOOL)inChuncks bangoUserId:(NSString *)bangoUserId {
    int notProfileId = 0;
    
    NSMutableArray *returnValue = [NSMutableArray array];
    BGOProfile *bangoProfile;
    
    @synchronized(self) {
        if (notCurrentProfile) {
            notProfileId = _profileId;
        }
        
        [self openDatabase];
        _openProfilesForPost = true;
        
        BGOFMResultSet *profiles = [_database executeQuery:[NSString stringWithFormat:DATABASE_SELECTPOSTPROFILES,
                                                         self.profileTableName],
                                 [NSNumber numberWithInt:notProfileId]];
        
        if (profiles != nil) {
            while ([profiles next]) {
                bangoProfile = [BGOProfile profile];
                bangoProfile.id = [profiles intForColumn:DATABASE_COLUMN_PROFILEID];
                bangoProfile.sdkType = [profiles intForColumn:DATABASE_COLUMN_SDKTYPE];
                bangoProfile.sdkVersion = [profiles stringForColumn:DATABASE_COLUMN_SDKVERSION];
                bangoProfile.applicationKey = [profiles stringForColumn:DATABASE_COLUMN_APPLICATIONID];
                bangoProfile.applicationType = [profiles stringForColumn:DATABASE_COLUMN_APPLICATIONTYPE];
                bangoProfile.applicationChannel = [profiles stringForColumn:DATABASE_COLUMN_APPLICATIONCHANNEL];
                bangoProfile.applicationVersion = [profiles stringForColumn:DATABASE_COLUMN_APPLICATIONVERSION];
                bangoProfile.userId = [profiles stringForColumn:DATABASE_COLUMN_USERID];
                bangoProfile.sessionRestartTime = [profiles intForColumn:DATABASE_COLUMN_SESSIONRESTARTTIME];
                bangoProfile.created = [profiles dateForColumn:DATABASE_COLUMN_CREATED];
                bangoProfile.connectionType = [profiles stringForColumn:DATABASE_COLUMN_CONNECTIONTYPE];
                bangoProfile.isRoaming = [profiles intForColumn:DATABASE_COLUMN_ISROAMING] == 0 ? NO : YES;
                bangoProfile.locationSource = [profiles stringForColumn:DATABASE_COLUMN_LOCATIONSOURCE];
                bangoProfile.locationAltitude = [profiles doubleForColumn:DATABASE_COLUMN_LOCATIONALTITUDE];
                bangoProfile.locationLatitude = [profiles doubleForColumn:DATABASE_COLUMN_LOCATIONLATITUDE];
                bangoProfile.locationLongitude = [profiles doubleForColumn:DATABASE_COLUMN_LOCATIONLONGITUDE];
                bangoProfile.locationAccuracy = [profiles doubleForColumn:DATABASE_COLUMN_LOCATIONACCURACY];
                bangoProfile.networkOperator = [profiles stringForColumn:DATABASE_COLUMN_OPERATOR];
                bangoProfile.operatorName = [profiles stringForColumn:DATABASE_COLUMN_OPERATORNAME];
                bangoProfile.operatorCountryIso = [profiles stringForColumn:DATABASE_COLUMN_OPERATORCOUNTRYISO];
                bangoProfile.sim = [profiles stringForColumn:DATABASE_COLUMN_SIMOPERATOR];
                bangoProfile.brand = [profiles stringForColumn:DATABASE_COLUMN_BRAND];
                bangoProfile.device = [profiles stringForColumn:DATABASE_COLUMN_DEVICE];
                bangoProfile.model = [profiles stringForColumn:DATABASE_COLUMN_MODEL];
                bangoProfile.product = [profiles stringForColumn:DATABASE_COLUMN_PRODUCT];
                bangoProfile.version = [profiles stringForColumn:DATABASE_COLUMN_VERSION];
                bangoProfile.buildId = [profiles stringForColumn:DATABASE_COLUMN_BUILDID];
                bangoProfile.operatorConnectionType = [profiles stringForColumn:DATABASE_COLUMN_OPERATORCONNECTIONTYPE];
                bangoProfile.isProfilePosted = [profiles intForColumn:DATABASE_COLUMN_PROFILEPOSTED] == 0 ? NO : YES;
                bangoProfile.bangoUserId = bangoUserId;
                
                [self addEventsToProfileForPost:bangoProfile chunked:inChuncks];
                
                [returnValue addObject:bangoProfile];
            }
        }
        
        [profiles close];
        
        
        _openProfilesForPost = false;
        
        [self closeDatabase];
    }
    
    return returnValue;
}

-(int)getEventCount{
    int eventCount = 0;
    BGOFMResultSet *count = nil;
    @synchronized(self){
        [self openDatabase];
        
        count = [_database executeQuery:[NSString stringWithFormat:DATABASE_COUNT_ALL_EVENTS, self.eventTableName]];
        
        if(count != nil){
            while ([count next]) {
                eventCount = [count intForColumn:DATABASE_COLUMN_EVENT_COUNT];
            }
            [count close];
        }
        else{
            eventCount = 0;
        }
        [self closeDatabase];
    }
    
    return eventCount;
}

-(void)addEventsToProfileForPost:(BGOProfile *)profile chunked:(BOOL)inChunks {
    BGOEvent *bangoEvent;
    BGOEventParameter *bangoEventParameter;
    BGOFMResultSet *events = nil;
    BGOFMResultSet *eventParameters = nil;
    
    @synchronized(self) {
        [self openDatabase];
        
        _openEventsForPost = true;
        
        
        events = [_database executeQuery:[NSString stringWithFormat:inChunks ? DATABASE_SELECTPOSTEVENTS : DATABASE_SELECTPOSTEVENTS_ALL,
                                          self.eventTableName, 
                                          _postChunkSize <= MIN_POST_CHUNK_SIZE || _postChunkSize > MAX_POST_CHUNK_SIZE ? DEFAULT_POST_CHUNK_SIZE : _postChunkSize],
                  [NSNumber numberWithInt:profile.id]];
        
        if (events != nil) {
            while ([events next]) {
                bangoEvent = [BGOEvent event];
                bangoEvent.id = [events intForColumn:DATABASE_COLUMN_EVENTID];
                bangoEvent.eventName = [events stringForColumn:DATABASE_COLUMN_EVENTNAME];
                bangoEvent.eventDetail = [events stringForColumn:DATABASE_COLUMN_EVENTDETAIL];
                bangoEvent.eventValue = [events stringForColumn:DATABASE_COLUMN_EVENTVALUE];
                bangoEvent.errorId = [events stringForColumn:DATABASE_COLUMN_ERRORID];
                bangoEvent.eventTime = [NSDate dateWithTimeIntervalSince1970:[events longForColumn:DATABASE_COLUMN_EVENTTIME]];
                bangoEvent.connectionType = [events stringForColumn:DATABASE_COLUMN_CONNECTIONTYPE];
                bangoEvent.operatorConnectionType = [events stringForColumn:DATABASE_COLUMN_OPERATORCONNECTIONTYPE];
                bangoEvent.errorLevel = [events intForColumn:DATABASE_COLUMN_ERRORLEVEL];
                bangoEvent.eventPosted = [events intForColumn:DATABASE_COLUMN_EVENTPOSTED] == 0 ? NO : YES;
                
                eventParameters = [_database executeQuery:[NSString stringWithFormat:DATABASE_SELECTPOSTEVENTPARAMETERS, self.eventParameterTableName],
                                   [NSNumber numberWithInt:bangoEvent.id]];
                if (eventParameters != nil) {
                    while ([eventParameters next]) {
                        bangoEventParameter = [BGOEventParameter eventParameter];
                        bangoEventParameter.id = [events intForColumn:DATABASE_COLUMN_EVENTID];
                        bangoEventParameter.eventName = [eventParameters stringForColumn:DATABASE_COLUMN_EVENTNAME];
                        bangoEventParameter.eventValue = [eventParameters stringForColumn:DATABASE_COLUMN_EVENTVALUE];
                        [bangoEvent addToEventParameters:bangoEventParameter];
                    }
                }
                
                [eventParameters close];
                
                [profile addToEvents:bangoEvent];
            }
            
            
            [events close];
        }
        
        [self closeDatabase];
    }
}

-(BOOL)isCurrentProfile:(int)profileId {
    return _profileId == profileId;
}

-(void)dealloc {
    [_database release];
    [_insertProfileStatement release];
    [_insertEventStatement release];
    [_insertEventParameterStatement release];
    [super dealloc];
}

#pragma mark - Private methods

-(NSString *)applicationDocumentsDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                NSUserDomainMask, YES)
            lastObject];
}

- (NSString *)applicationCacheDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
                                                NSUserDomainMask, YES)
            lastObject];
}

#pragma mark - shared persistence manager

static BGOPersistenceManager *sharedPersistenceManager = nil; 

+ (BGOPersistenceManager *)sharedPersistenceManager
{ 
	@synchronized(self) { 
		if (sharedPersistenceManager == nil) { 
			sharedPersistenceManager = [[self alloc] init]; 
		} 
	} 
    
	return sharedPersistenceManager; 
} 

+ (id)allocWithZone:(NSZone *)zone 
{ 
	@synchronized(self) { 
		if (sharedPersistenceManager == nil) { 
			sharedPersistenceManager = [super allocWithZone:zone]; 
			return sharedPersistenceManager; 
		} 
	} 
    
	return nil; 
} 

- (id)copyWithZone:(NSZone *)zone 
{ 
	return self; 
} 

- (id)retain 
{ 
	return self; 
} 

- (NSUInteger)retainCount 
{ 
	return NSUIntegerMax; 
} 

- (oneway void)release 
{ 
} 

- (id)autorelease 
{ 
	return self; 
}

@end
